from datetime import datetime, timedelta
import logging
from odoo import api, fields, tools, models, _
from odoo.exceptions import UserError, ValidationError
import os

_logger = logging.getLogger(__name__)


class ProductExt(models.Model):
    _inherit = 'product.template'

    name = fields.Char(translate=False, index=True)
    uom_value_ids = fields.One2many('product.uom.value', 'product_id', 'Product Uom Value', copy=True,
                                    ondelete='cascade')
    is_manufacture = fields.Boolean("Is Manufacture", compute="_compute_is_manufacture")

    is_purchase = fields.Boolean("Is Purchase", compute="_compute_is_purchase")
    param_line_ids = fields.One2many('product.template.param.line', 'product_tmpl_id', 'Product Params', copy=True,
                                     ondelete='cascade')
    pur_param_line_ids = fields.One2many(
        'product.template.pur.param.line', 'product_tmpl_id',
        'Product Purchase Params', copy=True, ondelete='cascade'
    )
    spec_line_ids = fields.One2many(
        'product.template.spec.line', 'product_tmpl_id', 'Product Specifications',
        copy=True, ondelete='cascade'
    )
    value_pnl = fields.Float('PCS/PNL', compute='_compute_unit_value', digits='Product Unit of Measure', store=True)
    value_set = fields.Float('PCS/SET', compute='_compute_unit_value', digits='Product Unit of Measure', store=True)
    s_value_sheet = fields.Float('PNL/SHEET', compute='_compute_unit_value', digits='Product Unit of Measure',
                                 store=True)
    opt_lot_size = fields.Integer(string='Optimal Lot Size', default=0)
    scrap_rate = fields.Float(string='Scrap Rate', default=0)
    working_instruction = fields.Html('Working Instruction')
    crm_id = fields.Many2one('crm.lead', string="Crm")

    create_time = fields.Datetime(string="Create Date", default=fields.Datetime.now)
    update_time = fields.Datetime(string="Update Date", default=fields.Datetime.now)
    materiel_status = fields.Selection([
        ('S', 'NPI'),
        ('P', 'MP'),
        ('T', 'Test'),
        ('Q', 'QTA+NPI'),
    ], string='Product Status', tracking=True)
    versions = fields.Char('Versions')

    # 阻抗表 需要使用多对多方式
    impedance_ids = fields.Many2many(
        comodel_name='product.impedance',
        relation='product_template_impedance_rel',
        column1='product_tmpl_id',
        column2='impedance_id',
        string='Impedance')
    impedance_image = fields.Image(string='Impedance(draw)')
    cutting_drawing_image = fields.Image(string='Cutting Drawing')
    stack_up_image = fields.Image(string='stack-up')

    makeup_product_ids = fields.One2many('s.makeup.product', 'origin_product_tmpl_id')
    s_mi_change_resume_ids = fields.One2many('mi.change.resume', 'product_id', string='MI Change Resume')
    s_categ_domain = fields.Boolean(compute='_compute_s_categ_domain')
    s_utilization = fields.Char(compute='_compute_s_utilization', string='Utilization')

    s_jmi_upload = fields.Boolean(string='JMI Upload')

    # PP统计
    s_statistics_ids = fields.One2many('sb.pp.statistics', 'product_tmpl_id', string='PP statistics')

    def _compute_s_utilization(self):
        for res in self:
            res.s_utilization = ''
            if res.categ_id.name not in ['半成品', '成品']:
                continue
            # 取第一个 BOM
            bom = self.env['mrp.bom'].search([('product_tmpl_id', '=', res.id)], limit=1)
            if not bom:
                continue
            lowest_bom = self._get_lowest_level_bom(bom)
            # 查找 开料 利用率
            for op in lowest_bom.operation_ids:
                if op.workcenter_id.code != 'CUT01':
                    continue
                param = op.param_line_ids.filtered(lambda l: l.attribute_id.code == 'CUT01_18')
                if param and param[0].value_id:
                    res.s_utilization = param[0].value_id.name
                break

    def _get_lowest_level_bom(self, bom):
        """
        找出从当前 BOM 向下找的第一个最下层 BOM
        :param bom: 当前 BOM 记录
        :return: 最下层 BOM（单个）
        """
        # 取出 BOM 中为“半成品”的组件
        semi_finished_components = bom.bom_line_ids.filtered(
            lambda line: line.product_id.product_tmpl_id.categ_id.name == '半成品'
        ).mapped('product_id.product_tmpl_id')

        for component in semi_finished_components:
            # 查找这个半成品是否有自己的 BOM（只取一个）
            child_bom = self.env['mrp.bom'].search([
                ('product_tmpl_id', '=', component.id)
            ], limit=1)
            if child_bom:
                # 递归进入子 BOM
                return self._get_lowest_level_bom(child_bom)

        # 如果没有任何“半成品组件”或它们都没有 BOM，则当前 BOM 是最下层
        return bom

    @api.depends('categ_id')
    def _compute_s_categ_domain(self):
        for record in self:
            if not record.categ_id:
                record.s_categ_domain = False
                continue
            s = [self.env.ref('sb_sale.finished_product_type').id, self.env.ref('sb_sale.semi_product_type').id]
            record.s_categ_domain = record.categ_id.id in s

    @api.depends('name', 'default_code')
    def _compute_display_name(self):
        for rec in self:
            rec.display_name = rec.name

    @api.depends('uom_value_ids.uom_value')
    def _compute_unit_value(self):
        for record in self:
            uom_values = {x.uom_id.code: x.uom_value for x in record.uom_value_ids}
            value_pnl = uom_values.get('PANEL', 1)
            value_set = uom_values.get('SET', 1)
            s_value_sheet = uom_values.get('SHEET', 1)
            record.update({
                'value_pnl': value_pnl,
                'value_set': value_set,
                's_value_sheet': s_value_sheet
            })

    def _compute_is_purchase(self):
        _logger.info('_compute_is_purchase')
        buy_route = self.env.ref('purchase_stock.route_warehouse0_buy', raise_if_not_found=False)
        self.is_purchase = False
        if buy_route:
            route_ids = self.route_ids
            j = 0
            for o in route_ids:
                if o.id == buy_route.id:
                    j = j + 1
            if j > 0:
                self.is_purchase = True

    def _compute_is_manufacture(self):
        _logger.info('_compute_is_manufacture')
        route_ids = self.route_ids
        manufacture_route = self.env.ref('mrp.route_warehouse0_manufacture', raise_if_not_found=False)
        self.is_manufacture = False
        if manufacture_route:
            j = 0
            for o in route_ids:
                if o.id == manufacture_route.id:
                    j = j + 1
            if j > 0:
                self.is_manufacture = True

    @api.onchange('route_ids')
    def _onchange_route_ids(self):
        _logger.info('_onchange_route_ids')
        buy_route = self.env.ref('purchase_stock.route_warehouse0_buy', raise_if_not_found=False)
        manufacture_route = self.env.ref('mrp.route_warehouse0_manufacture', raise_if_not_found=False)
        route_ids = self.route_ids
        self.is_manufacture = False
        if manufacture_route:
            j = 0
            for o in route_ids:
                if manufacture_route.id in o.ids:
                    j = j + 1
            if j > 0:
                self.is_manufacture = True
                self._init_uov_records()
        j = 0
        self.is_purchase = False
        if buy_route:
            for o in route_ids:
                if o.id == buy_route.id:
                    j = j + 1
            if j > 0:
                self.is_purchase = True

    def _init_uov_records(self):
        icount = len(self.uom_value_ids)
        if icount == 0:
            lines = []
            sql = "SELECT * FROM uom_uom where category_id in (select id from uom_category where category_type='prod')"
            self.env.cr.execute(sql)
            dt = self.env.cr.dictfetchall()
            if dt:
                for item in dt:
                    litem = [(0, 0, {
                        'uom_id': item['id'],
                        'required': True,
                        'uom_value': 0,
                    })]
                    lines += litem
                self.uom_value_ids = lines

    def cron_product_uom_value(self):
        objs = self.sudo().search([])
        for i, obj in enumerate(objs):
            if not obj.uom_value_ids: continue
            pnll = {}
            pnlw = {}
            pnla = {}

            setl = {}
            setw = {}
            seta = {}

            pcsl = {}
            pcsw = {}
            pcsa = {}
            for puv in obj.uom_value_ids:
                if puv.uom_id.id == 30:
                    pnll = {puv: puv.uom_value}
                if puv.uom_id.id == 31:
                    pnlw = {puv: puv.uom_value}
                if puv.uom_id.id == 40:
                    pnla = {puv: puv.uom_value}

                if puv.uom_id.id == 33:
                    setl = {puv: puv.uom_value}
                if puv.uom_id.id == 32:
                    setw = {puv: puv.uom_value}
                if puv.uom_id.id == 38:
                    seta = {puv: puv.uom_value}

                if puv.uom_id.id == 35:
                    pcsl = {puv: puv.uom_value}
                if puv.uom_id.id == 34:
                    pcsw = {puv: puv.uom_value}
                if puv.uom_id.id == 39:
                    pcsa = {puv: puv.uom_value}
            try:
                if pnll and pnlw and pnla:
                    list(pnla.keys())[0].uom_value = round(list(pnll.values())[0] * list(pnlw.values())[0] / 1000000, 7)
                if setl and setw and seta:
                    # seta.keys()[0].uom_value = round(setl.keys()[0] * setw.keys()[0] / 1000000, 7)
                    list(seta.keys())[0].uom_value = round(list(setl.values())[0] * list(setw.values())[0] / 1000000, 7)

                if pcsl and pcsw and pcsa:
                    list(pcsa.keys())[0].uom_value = round(list(pcsl.values())[0] * list(pcsw.values())[0] / 1000000, 7)
                    # pcsa.keys()[0].uom_value = round(pcsl.keys()[0] * pcsw.keys()[0] / 1000000, 7)
            except Exception as e:
                _logger.info('更新失败产品{}'.format(obj.id))

    @api.model_create_multi
    def create(self, vals_list):
        res = super().create(vals_list)
        for obj in res:
            obj.check_product_name_code()
        return res

    def write(self, vals):
        res = super().write(vals)
        if 'name' in vals or 'default_code' in vals:
            self.check_product_name_code()
        if 'materiel_status' in vals:
            for rec in self:
                if not rec.categ_id.code:
                    continue
                if rec.categ_id.code != 'CHP':
                    continue
                product_bcp_ids = self.search([
                    ('serial_number', '=', rec.serial_number),
                    ('default_code', 'ilike', rec.default_code),
                    ('id', '!=', rec.id)
                ])
                product_bcp_ids.write({'materiel_status': rec.materiel_status})
        return res

    def check_product_name_code(self):
        """
        验证
        1. 编码必须唯一, 归档也算
        2. 名称和归档唯一
        """
        if not self.default_code:
            return
        flag = self.search([('id', '!=', self.id), ('default_code', '=', self.default_code), ('active', 'in', [True, False])])
        if flag:
            raise UserError('编码{}已存在'.format(self.default_code))
        flag = self.search([('id', '!=', self.id), ('name', '=', self.name)])
        if flag:
            raise UserError('名称{}已存在'.format(self.name))


class SbPPStatistics(models.Model):
    _name = 'sb.pp.statistics'
    _description = 'PP统计'

    # 产品
    product_tmpl_id = fields.Many2one('product.template', string="Product")
    # 绑定的部件（L4-L7）
    component = fields.Char(string="Component")
    # PP的开始层（1）
    start_layer = fields.Char(string="Start Layer")
    # PP的结束层（7）
    end_layer = fields.Char(string="End Layer")
    # PP的型号（7629）
    model = fields.Char(string="Model")
    # PP的张数（个）
    qty = fields.Integer(string="PP Qty")
